/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Ogre_Gfx_h__
#define __dylab_Ogre_Gfx_h__

//===========================================================================================
#include <dylab/simulation/ComGfx.h>
#include <dylab/utils/Timer.h>
#include <dylab/utils/containers/StackContainer.h>
#include <dylab/utils/containers/ListContainer.h>

//===========================================================================================
#include <ogre/Ogre.h>
#include <ogre/OgrePanelOverlayElement.h>
#include <ogre/RenderSystems/Direct3D9/OgreD3D9Plugin.h>
#include <ogre/RenderSystems/Gl/OgreGLPlugin.h>

//===========================================================================================
namespace dylab {

    /** 
     */
    class Ogre_Gfx 
        : public ComGfx
        , Ogre::LogListener
    {
    public:        
        static const String COMPONENT_NAME;

    public:
        static inline Ogre::Vector3 toOgreVector3(const Vector3 & _vec)
                { return Ogre::Vector3(_vec.x(), _vec.y(), _vec.z()); }
        static inline Vector3 toVector3(const Ogre::Vector3 & _vec)
                { return Vector3(_vec.x, _vec.y, _vec.z); }

        static inline Ogre::ColourValue toOgreColour(const Colour & _c)
                { return Ogre::ColourValue(_c.r(), _c.g(), _c.b(), _c.a()); }
        static inline Colour toColour(const Ogre::ColourValue & _c)
                { return Colour(_c.r, _c.g, _c.b, _c.a); }

    public:
        class Viewport 
            : public IViewport
        {
        private:
            uint32_t mZOrder;
            Ogre::Viewport * mOgreViewport;

        public:
            Viewport(uint32_t _zOrder, IWindow * _window, ICamera * _camera, 
                     real_t _left, real_t _top, real_t _width, real_t _height);
            virtual ~Viewport();

            virtual uint32_t getZOrder() const;

            virtual void setBackgroundColour(const Colour & _colour);
            virtual Vector2 getSize() const;
        };
        //-----------------------------------------------------------------------------

    public:
        class Mesh 
            : public IMesh
        {
        private:
            Ogre::MeshPtr mOgreMesh;

        public:
            Mesh(Ogre::MeshPtr _ogreMesh);
            virtual ~Mesh();

            virtual String getName() const;
        };
        //-----------------------------------------------------------------------------

    public:
        class MeshManager 
            : public IMeshManager
        {
        private:
            Ogre::MeshManager * mOgreMeshMgr;

            // all created meshes
            typedef HashObjContainer<String, Mesh> MeshMap;
            MeshMap mMeshMap;

        public:
            MeshManager();
            virtual ~MeshManager();

            virtual IMesh * createMesh(const String & _name);
            virtual IMesh * getMesh(const String & _name);
            virtual const IMesh * getMesh(const String & _name) const;
            virtual void destroyMesh(const String & _name);

            virtual IMesh * createPlane(const String & _name, const Plane & _plane, const Vector2 & _size, 
                                        const Vector2 & _segments, const Vector2 & _tiles);
        };
        //-----------------------------------------------------------------------------

    public:
        class Light 
            : public ILight
        {
        private:
            type_t mType;
            Ogre::Light * mOgreLight;

        public:
            Light(const String & _name, IScene * _scene, type_t _type);
            virtual ~Light();

            virtual String getName() const;
            virtual type_t getType() const;

            virtual void setPosition(const Vector3 & _position);
            virtual Vector3 getPosition() const;

            virtual void setDirection(const Vector3 & _position);
            virtual Vector3 getDirection() const;

            virtual void setDiffuseColour(const Colour & _colour);
            virtual Colour getDiffuseColour() const;

            virtual void setSpecularColour(const Colour & _colour);
            virtual Colour getSpecularColour() const;

            virtual void setAttenuation(real_t _range, real_t _constant, real_t _linear, real_t _quadratic);
            virtual void setCastShadows(bool _castShadows);
        };
        //-----------------------------------------------------------------------------

    public:
        class Camera 
            : public ICamera
        {
        private:
            Ogre::Camera * mOgreCamera;

        public:
            Camera(const String & _name, IScene * _scene);
            virtual ~Camera();

            virtual String getName() const;
            virtual void setPosition(const Vector3 & _pos);            

            virtual void move(const Vector3 & _pos);            
            virtual void rotate(real_t _yaw, real_t _pitch, real_t _roll);
            virtual void lookAt(const Vector3 & target);

            virtual polygon_mode_t getPolygonMode() const;
            virtual void setPolygonMode(polygon_mode_t _polygonMode);

            virtual void setNearClipDistance(real_t _distance);
            virtual void setFarClipDistance(real_t _distance);
            virtual void setAspectRatio(real_t _ratio);

            Ogre::Camera * getOgreCamera()
                    { return mOgreCamera; }
        };
        //-----------------------------------------------------------------------------

    public:
        class Window 
            : public IWindow
        {
        private:
            Ogre::RenderWindow * mOgreWindow;
            Ogre_Gfx * mGfx;

            // all created viewports & the default one (first created)
            typedef HashObjContainer<uint32_t, Viewport> ViewportMap;
            ViewportMap mViewportMap;
            Viewport * mDefaultViewport;

        public:
            Window(const String & _title, Ogre_Gfx * _gfx);
            virtual ~Window();

            virtual String getName() const;

            virtual void getMetrics(uint32_t & _width, uint32_t & _height, uint32_t & _colourDepth, int32_t & _left, int32_t & _top) const;
            virtual void getStatistics(uint32_t & _lastFPS, uint32_t & _bestFPS, uint32_t & _worstFPS, uint32_t & _triangleCount) const; 
            virtual void getPlatformHandle(void * _handle) const;

            virtual void setPosition(const Vector2 & _pos);

            virtual IViewport * 
                createViewport(uint32_t _zOrder, ICamera * _renderCamera = NULL, 
                               const Vector2 & _position = Vector2::ZERO, const Vector2 & _size = Vector2(1, 1));
            virtual IViewport * getViewport(uint32_t _zOrder = IViewport::DEFAULT_ZORDER);
            virtual const IViewport * getViewport(uint32_t _zOrder = IViewport::DEFAULT_ZORDER) const;
            virtual void destroyViewport(uint32_t _zOrder = IViewport::DEFAULT_ZORDER);

            Ogre::RenderWindow * getOgreRenderWindow()
                    { return mOgreWindow; }
        };
        //-----------------------------------------------------------------------------

    public:
        class RayQuery
            : public IRayQuery
        {
        private:
            Ogre::RaySceneQuery * mOgreRaySceneQuery;
            Viewport * mTargetViewport;
            Camera * mUsedCamera;
            String mName;

        public:
            RayQuery(const String & _name, uint32_t _rayQueryFlags, IScene * _scene, Viewport * _targetViewport, Camera * _usedCamera);
            virtual ~RayQuery();

            virtual String getName() const;
            virtual IResult * execute(uint32_t _screenX, uint32_t _screenY, Vector3 * _intersect = NULL);

            Ogre::RaySceneQuery * getOgreRaySceneQuery()
                    { return mOgreRaySceneQuery; }
        };
        //-----------------------------------------------------------------------------

    public:
        class Object
            : public IObject
            , public Ogre::UserDefinedObject
        {
        private:
            Ogre::ManualObject * mOgreManualObject;
            IUserData * mUserData;

        public:
            Object(const String & _name, IScene * _scene);
            virtual ~Object();

            virtual String getName() const;

            virtual void setDynamic(bool _dynamic);
            virtual bool isDynamic() const;

            virtual void createSection(const String & _material, section_t _sectionType,
                                        const PositionBuffer & _positions, 
                                        const IndexBuffer & _indices,
                                        const NormalBuffer * _normals = NULL,
                                        const TexCoordBuffer * _texCoords = NULL,
                                        const ColourBuffer * _colours = NULL);
            virtual void updateSection(uint32_t _section,
                                        const PositionBuffer & _positions, 
                                        const IndexBuffer & _indices,
                                        const NormalBuffer * _normals = NULL,
                                        const TexCoordBuffer * _texCoords = NULL,
                                        const ColourBuffer * _colours = NULL);
            virtual void changeSectionMaterialName(uint32_t _section, const String & _material);

            virtual void setRayQueryFlags(uint32_t _flags);
            virtual uint32_t getRayQueryFlags() const;

            virtual void setCastShadows(bool _castShadows);
            virtual bool isCastingShadows() const;

            virtual void translate(const Vector3 & _v);
            virtual void rotate(const Vector3 & _axis, real_t _degrees);
            virtual void scale(const Vector3 & _scale);

            virtual void setUserData(IUserData * _userData);
            virtual IUserData * getUserData();

            Ogre::ManualObject * getOgreManualObject()
                    { return mOgreManualObject; }
        private:
            void _generate(const PositionBuffer & _positions, const IndexBuffer & _indices, const NormalBuffer * _normals,
                            const TexCoordBuffer * _texCoords, const ColourBuffer * _colours);

        };
        //-----------------------------------------------------------------------------

    public:
        class ObjectPool
            : public IObjectPool
        {
        private:
            String mName;
            uint32_t mMaxObjects;
            IScene * mParentScene;

            typedef StackContainer<String> FreeNames;
            FreeNames mFreeNames;

            typedef HashContainer<String, String> UsedNames;
            UsedNames mUsedNames;

            typedef ListContainer<String> CreatedObjectNames;
            CreatedObjectNames mCreatedObjectNames;

            typedef HashContainer<String, String> CustomNamesMap;
            CustomNamesMap mCustomNamesMap;

        public:
            ObjectPool(const String & _name, IScene * _scene, uint32_t _maxObjects);            
            virtual ~ObjectPool();

            virtual String getName() const;
            virtual IObject * newObject(const String & _name = String::EMPTY);
            virtual IObject * getObject(const String & _name);
            virtual const IObject * getObject(const String & _name) const;
            virtual void deleteObject(const String & _name);
            virtual void deleteAllObjects();
        };
        //-----------------------------------------------------------------------------

    public:
        class Entity
            : public IEntity
        {
        private:
            Ogre::Entity * mOgreEntity;

        public:
            Entity(const String & _name, const String & _meshName, IScene * _scene);
            virtual ~Entity();

            virtual String getName() const;

            virtual void translate(const Vector3 & _v);
            virtual void rotate(const Vector3 & _axis, real_t _degrees);
            virtual void scale(const Vector3 & _scale);

            virtual void setCastShadows(bool _castShadows);
            virtual bool isCastingShadows() const;

            virtual void setRayQueryFlags(uint32_t _flags);
            virtual uint32_t getRayQueryFlags() const;

            virtual void setMaterialName(const String & _material);
        };
        //-----------------------------------------------------------------------------

    public:
        class ModelRenderer
            : public IModelRenderer
            , public Ogre::UserDefinedObject
        {
        private:
            Ogre::ManualObject * mOgreManualObject;
            SimObject * mParentSimObject;
            render_t mRenderType;
            String mMaterialName;

        public:
            ModelRenderer(const String & _name, const String & _materialName, SimObject * _parentSimObject, IScene * _scene);
            virtual ~ModelRenderer();

            virtual String getName() const;
            virtual String getDescription() const;        

            virtual SimObject * getSimObject();

            virtual void setMaterialName(const String & _material);
            virtual String getMaterialName() const;

            virtual void setRenderType(render_t _renderType);
            virtual render_t getRenderType() const;

            virtual void setCastShadows(bool _castShadows);
            virtual bool isCastingShadows() const;

            virtual void translate(const Vector3 & _v);
            virtual void rotate(const Vector3 & _axis, real_t _degrees);
            virtual void scale(const Vector3 & _scale);

            void prepareForRendering();

        private:
            void _update();
            void _updateFromBoundaryMesh();
            void _updateFromTetrahedralVolume();
        };
        //-----------------------------------------------------------------------------

    public:
        class Scene
            : public IScene
        {
        private:
            Ogre::SceneManager * mOgreSceneMgr;

            // all created cameras & the default one (first created)
            typedef HashObjContainer<String, Camera> CameraMap;
            CameraMap mCameraMap;
            Camera * mDefaultCamera;

            // all created lights
            typedef HashObjContainer<String, Light> LightMap;
            LightMap mLightMap;

            // all created objects
            typedef HashObjContainer<String, Object> ObjectMap;
            ObjectMap mObjectMap;

            // all created objectPools
            typedef HashObjContainer<String, ObjectPool> ObjectPoolMap;
            ObjectPoolMap mObjectPoolMap;

            // all created entitys
            typedef HashObjContainer<String, Entity> EntityMap;
            EntityMap mEntityMap;

            // all created modelGfxs
            typedef HashObjContainer<String, ModelRenderer> ModelRendererMap;
            ModelRendererMap mModelRendererMap;

            // all created ray queries
            typedef HashObjContainer<String, RayQuery> RayQueryMap;
            RayQueryMap mRayQueryMap;

            Ogre_Gfx * mGfx;

        public:
            Scene(const String & _name, Ogre_Gfx * _gfx);
            virtual ~Scene();

            virtual String getName() const;

            virtual ICamera * createCamera(const String & _name);
            virtual ICamera * getCamera(const String & _name = DEFAULT_NAME);
            virtual const ICamera * getCamera(const String & _name = DEFAULT_NAME) const;
            virtual void destroyCamera(const String & _name = DEFAULT_NAME);

            virtual ILight * createLight(const String & _name, ILight::type_t _type);
            virtual ILight * getLight(const String & _name);
            virtual const ILight * getLight(const String & _name) const;
            virtual void destroyLight(const String & _name);

            virtual IObject * createObject(const String & _name);
            virtual IObject * getObject(const String & _name);
            virtual const IObject * getObject(const String & _name) const;
            virtual IObject * findObject(const String & _name);
            virtual void destroyObject(const String & _name);

            virtual IObjectPool * createObjectPool(const String & _name, uint32_t _maxObjects);
            virtual IObjectPool * getObjectPool(const String & _name);
            virtual const IObjectPool * getObjectPool(const String & _name) const;
            virtual void destroyObjectPool(const String & _name);

            virtual IEntity * createEntity(const String & _name, const String & _meshName);
            virtual IEntity * getEntity(const String & _name);
            virtual const IEntity * getEntity(const String & _name) const;
            virtual void destroyEntity(const String & _name);

            virtual IRayQuery * createRayQuery(const String & _name, uint32_t _queryFlags, ICamera * _usedCamera = NULL, IViewport * _targetViewport = NULL);
            virtual IRayQuery * getRayQuery(const String & _name);
            virtual const IRayQuery * getRayQuery(const String & _name) const;
            virtual void destroyRayQuery(const String & _name);

            virtual IModelRenderer * createModelRenderer(const String & _name, const String & _materialName, SimObject * _parentSimObject);
            virtual IModelRenderer * getModelRenderer(const String & _name);
            virtual const IModelRenderer * getModelRenderer(const String & _name) const;
            virtual IModelRenderer * findModelRenderer(const String & _name);
            virtual void destroyModelRenderer(const String & _name);

            virtual void setAmbientLight(const Colour & _light);
            virtual Colour getAmbientLight() const;

            virtual void setShadowType(shadow_t _shadowType);
            virtual shadow_t getShadowType() const;

            virtual void showBoundingBoxes(bool _show);
            virtual bool areBoundingBoxesVisible() const;

            void prepareForRendering();

            Ogre::SceneManager * getOgreSceneMgr()
                    { return mOgreSceneMgr; }
        };
        //-----------------------------------------------------------------------------

    public:
        class GuiSystem
            : public IGuiSystem
        {
        private:            
            String mName;
            Vector2 mMousePos;
            Window * mWindow;
            Scene * mScene;

            Ogre::PanelOverlayElement * mMousePanel;
            Ogre::MaterialPtr mMouseMaterial;
            Ogre::TexturePtr mMouseTexture;

        public:
            GuiSystem(const String & _name, Window * _window, Scene * _scene);
            virtual ~GuiSystem();

            virtual String getName() const;

            virtual void setMousePosition(const Vector2  & _relPos);
            virtual void setMouseImage(const String & _fileName);
            virtual void showMouse(bool _visible);
            virtual bool isMouseVisible() const;            

            virtual void showGui(const String & _name, bool _show);
            virtual bool isGuiVisible(const String & _name) const;

            virtual void setGuiElementCaption(const String & _elemName, const String & _caption);
        };
        //-----------------------------------------------------------------------------

    private:

        // ogre root object & plug-ins
        Ogre::Root * mRoot;
        Ogre::D3D9Plugin * mD3D9Plugin;
        Ogre::GLPlugin * mGLPlugin;

        // all created scenes & the default one (first created)
        typedef HashObjContainer<String, Scene> SceneMap;
        SceneMap mSceneMap;    
        Scene * mDefaultScene;

        // the default window (first created)
        Window * mDefaultWindow;

        // all create gui systems
        typedef HashObjContainer<String, GuiSystem> GuiSystemMap;
        GuiSystemMap mGuiSystemMap;
        GuiSystem * mDefaultGuiSystem;

        // mesh manager is signelton
        MeshManager * mMeshManager;

        // our event dispatcher
        Simulation * mParentSimulation;

        // timer for render event time meassurement
        Timer mTimer;

    public:
        Ogre_Gfx();
        virtual ~Ogre_Gfx();

        virtual void messageLogged(const Ogre::String & message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String & logName);

        virtual String getDescription() const;

        virtual void initialize(Simulation * _parentSimulation);
		virtual void loadFromPropertyTree(const PropertyTree & _pt);
        virtual Simulation * getParentSimulation();
        virtual const Simulation * getParentSimulation() const;

        virtual IWindow * createWindow(const String & _title);
        virtual IWindow * getWindow();
        virtual const IWindow * getWindow() const;

        virtual IScene * createScene(const String & _name);
        virtual IScene * getScene(const String & _name = DEFAULT_NAME);
        virtual const IScene * getScene(const String & _name = DEFAULT_NAME) const;
        virtual void renderScene(const String & _name = DEFAULT_NAME);
        virtual void destroyScene(const String & _name = DEFAULT_NAME);

        virtual IGuiSystem * createGuiSystem(const String & _name, IWindow * _parentWindow = NULL, IScene * _parentScene = NULL);
        virtual IGuiSystem * getGuiSystem(const String & _name = DEFAULT_NAME);
        virtual const IGuiSystem * getGuiSystem(const String & _name = DEFAULT_NAME) const;
        virtual void destroyGuiSystem(const String & _name = DEFAULT_NAME);

        virtual IMeshManager * getMeshManager();
        virtual const IMeshManager * getMeshManager() const;

        virtual uint32_t getCurrentFrameNumber() const;

	public:
        void setRenderSystemParam(const String & _renderSys, const String & _paramName, const String & _paramValue);
        void setActiveRenderSystem(const String & _renderSys);
        void addResourceArchive(const String & _section, const String & _type, const String & _archive);
        void initAllResourceGroups();
    };
}
//===========================================================================================
#endif // __dylab_Ogre_Gfx_h__